/*-------------------------------------------------------------------------
 * drawElements Quality Program Test Executor
 * ------------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Merge two test logs.
 *
 * \todo [2013-11-08 pyry] Write variant that can operate with less memory.
 *//*--------------------------------------------------------------------*/

#include "xeTestLogParser.hpp"
#include "xeTestResultParser.hpp"
#include "xeTestLogWriter.hpp"
#include "deString.h"

#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <stdexcept>

using std::vector;
using std::string;
using std::set;
using std::map;

enum Flags
{
	FLAG_USE_LAST_INFO = (1<<0)
};

struct CommandLine
{
	CommandLine (void)
		: flags(0)
	{
	}

	vector<string>	srcFilenames;
	string			dstFilename;
	deUint32		flags;
};

class LogHandler : public xe::TestLogHandler
{
public:
	LogHandler (xe::BatchResult* batchResult, deUint32 flags)
		: m_batchResult	(batchResult)
		, m_flags		(flags)
	{
	}

	void setSessionInfo (const xe::SessionInfo& info)
	{
		xe::SessionInfo& combinedInfo = m_batchResult->getSessionInfo();

		if (m_flags & FLAG_USE_LAST_INFO)
		{
			if (!info.targetName.empty())		combinedInfo.targetName			= info.targetName;
			if (!info.releaseId.empty())		combinedInfo.releaseId			= info.releaseId;
			if (!info.releaseName.empty())		combinedInfo.releaseName		= info.releaseName;
			if (!info.candyTargetName.empty())	combinedInfo.candyTargetName	= info.candyTargetName;
			if (!info.configName.empty())		combinedInfo.configName			= info.configName;
			if (!info.resultName.empty())		combinedInfo.resultName			= info.resultName;
			if (!info.timestamp.empty())		combinedInfo.timestamp			= info.timestamp;
		}
		else
		{
			if (combinedInfo.targetName.empty())		combinedInfo.targetName			= info.targetName;
			if (combinedInfo.releaseId.empty())			combinedInfo.releaseId			= info.releaseId;
			if (combinedInfo.releaseName.empty())		combinedInfo.releaseName		= info.releaseName;
			if (combinedInfo.candyTargetName.empty())	combinedInfo.candyTargetName	= info.candyTargetName;
			if (combinedInfo.configName.empty())		combinedInfo.configName			= info.configName;
			if (combinedInfo.resultName.empty())		combinedInfo.resultName			= info.resultName;
			if (combinedInfo.timestamp.empty())			combinedInfo.timestamp			= info.timestamp;
		}
	}

	xe::TestCaseResultPtr startTestCaseResult (const char* casePath)
	{
		if (m_batchResult->hasTestCaseResult(casePath))
		{
			xe::TestCaseResultPtr existingResult = m_batchResult->getTestCaseResult(casePath);
			existingResult->clear();
			return existingResult;
		}
		else
			return m_batchResult->createTestCaseResult(casePath);
	}

	void testCaseResultUpdated (const xe::TestCaseResultPtr&)
	{
		// Ignored.
	}

	void testCaseResultComplete (const xe::TestCaseResultPtr&)
	{
		// Ignored.
	}

private:
	xe::BatchResult* const	m_batchResult;
	const deUint32			m_flags;
};

static void readLogFile (xe::BatchResult* dstResult, const char* filename, deUint32 flags)
{
	std::ifstream		in				(filename, std::ifstream::binary|std::ifstream::in);
	LogHandler			resultHandler	(dstResult, flags);
	xe::TestLogParser	parser			(&resultHandler);
	deUint8				buf				[2048];
	int					numRead			= 0;

	if (!in.good())
		throw std::runtime_error(string("Failed to open '") + filename + "'");

	for (;;)
	{
		in.read((char*)&buf[0], DE_LENGTH_OF_ARRAY(buf));
		numRead = (int)in.gcount();

		if (numRead <= 0)
			break;

		parser.parse(&buf[0], numRead);
	}

	in.close();
}

static void mergeTestLogs (const CommandLine& cmdLine)
{
	xe::BatchResult batchResult;

	for (vector<string>::const_iterator filename = cmdLine.srcFilenames.begin(); filename != cmdLine.srcFilenames.end(); ++filename)
		readLogFile(&batchResult, filename->c_str(), cmdLine.flags);

	if (!cmdLine.dstFilename.empty())
		xe::writeBatchResultToFile(batchResult, cmdLine.dstFilename.c_str());
	else
		xe::writeTestLog(batchResult, std::cout);
}

static void printHelp (const char* binName)
{
	printf("%s: [filename] [[filename 2] ...]\n", binName);
	printf("  --dst=[filename]    Write final log to file, otherwise written to stdout.\n");
	printf("  --info=[first|last] Select which session info to use (default: first).\n");
}

static bool parseCommandLine (CommandLine& cmdLine, int argc, const char* const* argv)
{
	for (int argNdx = 1; argNdx < argc; argNdx++)
	{
		const char* arg = argv[argNdx];

		if (!deStringBeginsWith(arg, "--"))
			cmdLine.srcFilenames.push_back(arg);
		else if (deStringBeginsWith(arg, "--dst="))
		{
			if (!cmdLine.dstFilename.empty())
				return false;
			cmdLine.dstFilename = arg+6;
		}
		else if (deStringEqual(arg, "--info=first"))
			cmdLine.flags &= ~FLAG_USE_LAST_INFO;
		else if (deStringEqual(arg, "--info=last"))
			cmdLine.flags |= FLAG_USE_LAST_INFO;
		else
			return false;
	}

	if (cmdLine.srcFilenames.empty())
		return false;

	return true;
}

int main (int argc, const char* const* argv)
{
	try
	{
		CommandLine cmdLine;

		if (!parseCommandLine(cmdLine, argc, argv))
		{
			printHelp(argv[0]);
			return -1;
		}

		mergeTestLogs(cmdLine);
	}
	catch (const std::exception& e)
	{
		printf("FATAL ERROR: %s\n", e.what());
		return -1;
	}

	return 0;
}
